package main

import (
	"runtime"

	"gitee.com/dalixu/lab/oglutil"
	"github.com/go-gl/gl/v4.6-core/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/go-gl/mathgl/mgl32"
)

func init() {
	// This is needed to arrange that main() runs on main thread.
	// See documentation for functions that are only allowed to be called from the main thread.
	runtime.LockOSThread()
}

func main() {
	cameraPos := mgl32.Vec3{0.0, 0.0, 3.0}
	cameraFront := mgl32.Vec3{0.0, 0.0, -1.0}
	cameraUp := mgl32.Vec3{0.0, 1.0, 0.0}
	ca := oglutil.NewCamera(cameraPos, cameraFront, cameraUp)

	err := glfw.Init()
	if err != nil {
		panic(err)
	}
	defer glfw.Terminate()

	window, err := glfw.CreateWindow(640, 480, "Testing", nil, nil)
	if err != nil {
		panic(err)
	}
	window.MakeContextCurrent()
	window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
	window.SetCursorPosCallback(func(w *glfw.Window, xpos float64, ypos float64) {
		ca.Turn(float32(xpos), float32(ypos))
	})
	window.SetScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) {
		ca.Zoom(float32(yoff))
	})
	gl.Init()
	vertices := []float32{
		-0.5, -0.5, -0.5, 0.0, 0.0,
		0.5, -0.5, -0.5, 1.0, 0.0,
		0.5, 0.5, -0.5, 1.0, 1.0,
		0.5, 0.5, -0.5, 1.0, 1.0,
		-0.5, 0.5, -0.5, 0.0, 1.0,
		-0.5, -0.5, -0.5, 0.0, 0.0,

		-0.5, -0.5, 0.5, 0.0, 0.0,
		0.5, -0.5, 0.5, 1.0, 0.0,
		0.5, 0.5, 0.5, 1.0, 1.0,
		0.5, 0.5, 0.5, 1.0, 1.0,
		-0.5, 0.5, 0.5, 0.0, 1.0,
		-0.5, -0.5, 0.5, 0.0, 0.0,

		-0.5, 0.5, 0.5, 1.0, 0.0,
		-0.5, 0.5, -0.5, 1.0, 1.0,
		-0.5, -0.5, -0.5, 0.0, 1.0,
		-0.5, -0.5, -0.5, 0.0, 1.0,
		-0.5, -0.5, 0.5, 0.0, 0.0,
		-0.5, 0.5, 0.5, 1.0, 0.0,

		0.5, 0.5, 0.5, 1.0, 0.0,
		0.5, 0.5, -0.5, 1.0, 1.0,
		0.5, -0.5, -0.5, 0.0, 1.0,
		0.5, -0.5, -0.5, 0.0, 1.0,
		0.5, -0.5, 0.5, 0.0, 0.0,
		0.5, 0.5, 0.5, 1.0, 0.0,

		-0.5, -0.5, -0.5, 0.0, 1.0,
		0.5, -0.5, -0.5, 1.0, 1.0,
		0.5, -0.5, 0.5, 1.0, 0.0,
		0.5, -0.5, 0.5, 1.0, 0.0,
		-0.5, -0.5, 0.5, 0.0, 0.0,
		-0.5, -0.5, -0.5, 0.0, 1.0,

		-0.5, 0.5, -0.5, 0.0, 1.0,
		0.5, 0.5, -0.5, 1.0, 1.0,
		0.5, 0.5, 0.5, 1.0, 0.0,
		0.5, 0.5, 0.5, 1.0, 0.0,
		-0.5, 0.5, 0.5, 0.0, 0.0,
		-0.5, 0.5, -0.5, 0.0, 1.0,
	}
	cubePositions := []mgl32.Vec3{
		mgl32.Vec3{0.0, 0.0, 0.0},
		mgl32.Vec3{2.0, 5.0, -15.0},
		mgl32.Vec3{-1.5, -2.2, -2.5},
		mgl32.Vec3{-3.8, -2.0, -12.3},
		mgl32.Vec3{2.4, -0.4, -3.5},
		mgl32.Vec3{-1.7, 3.0, -7.5},
		mgl32.Vec3{1.3, -2.0, -2.5},
		mgl32.Vec3{1.5, 2.0, -2.5},
		mgl32.Vec3{1.5, 0.2, -1.5},
		mgl32.Vec3{-1.3, 1.0, -1.5},
	}
	var vbo, vao uint32
	gl.GenVertexArrays(1, &vao)
	gl.GenBuffers(1, &vbo)
	gl.BindVertexArray(vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
	gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.STATIC_DRAW)
	// gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo)
	// gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices)*4, gl.Ptr(indices), gl.STATIC_DRAW)
	gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0))
	gl.EnableVertexAttribArray(0)
	gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4))
	gl.EnableVertexAttribArray(1)
	texture1 := oglutil.NewTexture("./data/container.png")
	err = texture1.Gen(gl.REPEAT, gl.REPEAT, gl.LINEAR, gl.LINEAR)
	if err != nil {
		panic(err)
	}
	texture2 := oglutil.NewTexture("./data/awesomeface.png")
	err = texture2.Gen(gl.REPEAT, gl.REPEAT, gl.LINEAR, gl.LINEAR)
	if err != nil {
		panic(err)
	}
	shader := oglutil.NewShader("./shaders/vertex.vs", "./shaders/fragment.fs")
	err = shader.Compile()
	if err != nil {
		panic(err)
	}
	shader.Use()
	shader.SetInt("ourTexture", 0)
	shader.SetInt("texture2", 1)
	gl.Enable(gl.DEPTH_TEST)
	var deltaTime, lastFrame float32
	for !window.ShouldClose() {

		speed := 2.5 * deltaTime
		if window.GetKey(glfw.KeyW) == glfw.Press {
			ca.Move(oglutil.FORWARD, speed)
		} else if window.GetKey(glfw.KeyS) == glfw.Press {
			ca.Move(oglutil.BACKWARD, speed)
		} else if window.GetKey(glfw.KeyA) == glfw.Press {
			ca.Move(oglutil.LEFT, speed)
		} else if window.GetKey(glfw.KeyD) == glfw.Press {
			ca.Move(oglutil.RIGHT, speed)
		}

		// Do OpenGL stuff.
		gl.ClearColor(0.2, 0.3, 0.3, 1.0)
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		gl.ActiveTexture(gl.TEXTURE0)
		gl.BindTexture(gl.TEXTURE_2D, texture1.ID)
		gl.ActiveTexture(gl.TEXTURE1)
		gl.BindTexture(gl.TEXTURE_2D, texture2.ID)
		shader.Use()
		projection := ca.Projection(float32(640)/480, 0.1, 100.0)
		view := ca.View()
		shader.SetM4("view", 1, &view[0])
		shader.SetM4("projection", 1, &projection[0])
		gl.BindVertexArray(vao)
		for i := 0; i < 10; i++ {
			model := mgl32.Ident4()
			vec := cubePositions[i]
			model = model.Mul4(mgl32.Translate3D(vec[0], vec[1], vec[2]))
			if i%3 == 0 {
				model = model.Mul4(mgl32.HomogRotate3D(float32(glfw.GetTime())*mgl32.DegToRad(-55.0), mgl32.Vec3{1.0, 0.0, 0.0}))
			}
			shader.SetM4("model", 1, &model[0])
			gl.DrawArrays(gl.TRIANGLES, 0, 36)
		}
		window.SwapBuffers()
		glfw.PollEvents()
		currentFrame := float32(glfw.GetTime())
		deltaTime, lastFrame = currentFrame-lastFrame, currentFrame
	}
	gl.DeleteVertexArrays(1, &vao)
	gl.DeleteBuffers(1, &vbo)
	//gl.DeleteBuffers(1, &ebo)
}
